﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

namespace K9Nano.Domain
{
    public abstract class RepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey>
        where TEntity : class, IEntity<TKey>
        where TKey : IEquatable<TKey>
    {
        public abstract IQueryable<TEntity> Query();

        public abstract IQueryable<TEntity> QueryAsNoTracking();

        public virtual TEntity Get(TKey id) => FirstOrDefault(x => x.Id.Equals(id));

        public virtual ValueTask<TEntity> GetAsync(TKey id, CancellationToken cancellation = default)
            => FirstOrDefaultAsync(x => x.Id.Equals(id), cancellation);

        public abstract IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors);

        public virtual List<TEntity> GetAllList() => Query().ToList();

        public abstract ValueTask<List<TEntity>> GetAllListAsync(CancellationToken cancellation = default);

        public virtual List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
            => Query().Where(predicate).ToList();

        public abstract ValueTask<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        public abstract ValueTask<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IQueryable<TEntity>> orderByCreator, int skip, int take,
            CancellationToken cancellation = default);

        public abstract ValueTask<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> orderBy, int skip, int take,
            CancellationToken cancellation = default);

        public abstract ValueTask<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, object>> orderBy, int skip, int take,
            CancellationToken cancellation = default);

        public virtual TEntity Single(Expression<Func<TEntity, bool>> predicate)
            => Query().Single(predicate);

        public abstract ValueTask<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        public virtual TEntity FirstOrDefault(TKey id)
            => Query().FirstOrDefault(x => x.Id.Equals(id));

        public virtual ValueTask<TEntity> FirstOrDefaultAsync(TKey id, CancellationToken cancellation = default)
            => FirstOrDefaultAsync(x => x.Id.Equals(id), cancellation);

        public virtual TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
            => Query().FirstOrDefault(predicate);

        public virtual TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> configOrderBy)
        {
            var query = Query();
            return configOrderBy(query).FirstOrDefault(predicate);
        }

        public abstract ValueTask<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        public abstract ValueTask<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> configOrderBy, CancellationToken cancellation = default);


        public virtual bool Any() => QueryAsNoTracking().Any();

        public virtual bool Any(Expression<Func<TEntity, bool>> predicate) => QueryAsNoTracking().Any(predicate);

        public abstract ValueTask<bool> AnyAsync(CancellationToken cancellation);

        public abstract ValueTask<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation);

        public abstract TEntity Insert(TEntity entity);

        public abstract ValueTask<TEntity> InsertAsync(TEntity entity, CancellationToken cancellation = default);

        public virtual TKey InsertAndGetId(TEntity entity)
        {
            return Insert(entity).Id;
        }

        public virtual async ValueTask<TKey> InsertAndGetIdAsync(TEntity entity, CancellationToken cancellation = default)
        {
            return (await InsertAsync(entity, cancellation)).Id;
        }

        public virtual TEntity InsertOrUpdate(TEntity entity)
        {
            return entity.IsTransient() ? Insert(entity) : Update(entity);
        }

        public virtual ValueTask<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellation = default)
        {
            return entity.IsTransient() ? InsertAsync(entity, cancellation) : UpdateAsync(entity, cancellation);
        }

        public virtual TKey InsertOrUpdateAndGetId(TEntity entity)
        {
            return InsertOrUpdate(entity).Id;
        }

        public virtual async ValueTask<TKey> InsertOrUpdateAndGetIdAsync(TEntity entity, CancellationToken cancellation = default)
        {
            return (await InsertOrUpdateAsync(entity, cancellation)).Id;
        }

        public abstract int AddRange(IEnumerable<TEntity> entities);

        public abstract ValueTask<int> AddRangeAsync(IEnumerable<TEntity> entities, CancellationToken cancellation = default);

        public abstract TEntity Update(TEntity entity);

        public abstract ValueTask<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellation = default);

        public abstract ValueTask UpdateAsync(Expression<Func<TEntity, bool>> predicate, TEntity updated, CancellationToken cancellation = default);

        public abstract void Delete(TEntity entity, bool soft = true);

        public abstract ValueTask DeleteAsync(TEntity entity, bool soft = true, CancellationToken cancellation = default);

        public virtual TEntity Delete(TKey id, bool soft = true)
        {
            var entity = Get(id);

            if (entity != null)
            {
                Delete(entity, soft);
            }

            return entity;
        }

        public virtual async ValueTask<TEntity> DeleteAsync(TKey id, bool soft = true, CancellationToken cancellation = default)
        {
            var entity = await GetAsync(id, cancellation);

            if (entity != null)
            {
                await DeleteAsync(entity, soft, cancellation);
            }

            return entity;
        }

        public abstract ValueTask DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool soft = true, int batchSize = 200, CancellationToken cancellation = default);

        public virtual int Count() => Query().Count();

        public abstract ValueTask<int> CountAsync(CancellationToken cancellation = default);

        public virtual int Count(Expression<Func<TEntity, bool>> predicate) => Query().Count(predicate);

        public abstract ValueTask<int> CountAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        public virtual long LongCount() => Query().LongCount();

        public abstract ValueTask<long> LongCountAsync(CancellationToken cancellation = default);

        public virtual long LongCount(Expression<Func<TEntity, bool>> predicate) => Query().LongCount(predicate);

        public abstract ValueTask<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        public abstract int Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        public abstract long Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        public abstract float Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        public abstract double Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        public abstract decimal Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        public abstract ValueTask<int> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        public abstract ValueTask<long> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        public abstract ValueTask<float> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        public abstract ValueTask<double> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        public abstract ValueTask<decimal> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);
        public abstract int Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        public abstract long Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        public abstract float Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        public abstract double Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        public abstract decimal Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        public abstract ValueTask<int> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        public abstract ValueTask<long> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        public abstract ValueTask<float> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        public abstract ValueTask<double> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        public abstract ValueTask<decimal> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);
        public abstract int Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        public abstract long Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        public abstract float Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        public abstract double Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        public abstract decimal Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        public abstract ValueTask<int> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        public abstract ValueTask<long> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        public abstract ValueTask<float> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        public abstract ValueTask<double> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        public abstract ValueTask<decimal> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);
        public abstract double Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        public abstract double Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        public abstract float Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        public abstract double Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        public abstract decimal Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        public abstract ValueTask<double> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        public abstract ValueTask<double> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        public abstract ValueTask<float> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        public abstract ValueTask<double> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        public abstract ValueTask<decimal> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);
    }
}